﻿#region License
/* Copyright (C) 2009 Tim Coulter
 * 
 * This file is part of ExtremeML.
 * 
 * ExtremeML is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation either version
 * 2 of the License, or (at your option) any later version.
 * 
 * ExtremeML is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/.
*/
#endregion

using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;

namespace ExtremeML.Builder
{
	/// <summary>
	/// An abstract base class for the creation of classes that exposes the design
	/// parameters for a generated concrete implementation of a wrapper class. 
	/// </summary>
	public abstract class WrapperModelBase
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="WrapperModelBase"/> class.
		/// </summary>
		/// <param name="wrappedType">The wrapped <see cref="Type"/>.</param>
		/// <param name="target">A <see cref="WrapperTypeInfo"/> that specifies the form of the generated class.</param>
		public WrapperModelBase(
			Type wrappedType,
			WrapperTypeInfo target)
		{
			WrappedType = wrappedType;
			Target = target;
		}

		/// <summary>
		/// Gets a <see cref="WrapperTypeInfo"/> that specifies the form of the generated class.
		/// </summary>
		public WrapperTypeInfo Target { get; private set; }

		/// <summary>
		/// Gets the wrapped <see cref="Type"/>.
		/// </summary>
		public Type WrappedType { get; private set; }

		/// <summary>
		/// Gets the set of <see cref="WrapperPropertyMapping"/> for this wrapper type.
		/// </summary>
		public IEnumerable<WrapperPropertyMapping> Properties
		{
			get { return GetProperties(); }
		}

		/// <summary>
		/// Gets the dictionary of custom patterns for this wrapper type.
		/// </summary>
		public Dictionary<string, CustomPattern> CustomPatterns
		{
			get { return GetCustomPatterns(); }
		}

		/// <summary>
		/// Returns whether the count of wrapper properties is greater than zero.
		/// </summary>
		public bool HasWrapperProperties()
		{
			return (from property in Properties
					where property.Category == PropertyCategory.PartWrapper ||
						property.Category == PropertyCategory.PartWrapperCollection ||
						property.Category == PropertyCategory.RootElementWrapper ||
						property.Category == PropertyCategory.ElementWrapper ||
						property.Category == PropertyCategory.IsolatedChildElementWrapper ||
						property.Category == PropertyCategory.ElementWrapperCollection
					select property).Count() > 0;
		}

		/// <summary>
		/// Returns the set of <see cref="WrapperPropertyMapping"/> for this wrapper type.
		/// </summary>
		protected abstract IEnumerable<WrapperPropertyMapping> GetProperties();

		/// <summary>
		/// Returns the dictionary of custom patterns for this wrapper type.
		/// </summary>
		protected virtual Dictionary<string, CustomPattern> GetCustomPatterns()
		{
			return Lookup.GetExplicitPatterns(Target.FullName);
		}

		/// <summary>
		/// Returns a <see cref="WrapperPropertyMapping"/> for the wrapped
		/// property that corresponds to the passed <see cref="PropertyInfo"/>.
		/// </summary>
		protected WrapperPropertyMapping CreatePropertyMapping(
			PropertyInfo info)
		{
			var type = info.PropertyType;
	
			var result = new WrapperPropertyMapping
			{
				MappedType = type,
				PropertyName = info.Name,
				MappedProperty = info.Name,
				CanGet = info.GetGetMethod() != null && info.GetGetMethod().IsPublic,
				CanSet = info.GetSetMethod() != null && info.GetSetMethod().IsPublic
			};

			if (IsIgnoredPropertyType(type) || Lookup.IsUnsupportedType(type))
			{
				result.Category = PropertyCategory.Ignore;
			}
			else if (Lookup.IsScalarType(type))
			{
				result.Category = PropertyCategory.Scalar;
				result.TypeName = Lookup.GetCSharpTypeName(type);
			}
			else if (Lookup.IsSimpleSdkType(type))
			{
				var mapping = Lookup.GetSimpleTypeMapping(type);
				result.Category = PropertyCategory.Simple;
				result.TypeName = Lookup.GetGenericExpandedName(mapping.FrameworkType);
				result.LoadExpression = mapping.ToFrameworkTypeExpression;
				result.SaveExpression = mapping.ToSdkTypeExpression;
			}
			else if (Lookup.IsEnumSdkType(type))
			{
				result.Category = PropertyCategory.Enum;
				result.TypeName = Lookup.GetGenericExpandedName(Lookup.GetEnumFrameworkType(type));
			}
			else if (Lookup.IsPartType(type))
			{
				result.Category = PropertyCategory.PartWrapper;
				result.TypeName = new WrapperTypeInfo(type, false).FullName;
			}
			else if (Lookup.IsPartCollectionType(type))
			{
				result.Category = PropertyCategory.PartWrapperCollection;
				result.TypeName = new WrapperTypeInfo(type, true).FullName;
			}
			else if (Lookup.IsRootElementType(type))
			{
				result.Category = PropertyCategory.RootElementWrapper;
				result.TypeName = new WrapperTypeInfo(type, false).FullName;
			}
			else if (Lookup.IsElementType(type))
			{
				result.Category = PropertyCategory.ElementWrapper;
				result.TypeName = new WrapperTypeInfo(type, false).FullName;
			}
			else
			{
				result.Category = PropertyCategory.Unsupported;
			}

			return result;
		}

		/// <summary>
		/// When overridden in a derived class, returns whether
		/// properties of the passed <see cref="Type"/> should be ignored.
		/// </summary>
		protected virtual bool IsIgnoredPropertyType(
			Type type)
		{
			return false;
		}
	}
}
